package io.websocketdemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;


import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
import org.webrtc.Camera1Enumerator;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerator;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.EglBase;
import org.webrtc.Logging;
import org.webrtc.MediaConstraints;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RendererCommon;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoDecoderFactory;
import org.webrtc.VideoEncoderFactory;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;
import org.webrtc.voiceengine.WebRtcAudioRecord;


import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import android.Manifest;

import com.google.gson.Gson;

import io.websocketdemo.my.MyCameraEventsHandler;
import io.websocketdemo.my.MyCapturerObserver;
import io.websocketdemo.my.MyDefaultVideoEncoderFactory;
import io.websocketdemo.my.MyPeerConnectionObserver;
import io.websocketdemo.my.MySimpleSdpObserver;
import io.websocketdemo.my.MyWebsocketClient;
import io.websocketdemo.protocol.Answer;
import io.websocketdemo.protocol.JsonProtocolAnswer;
import io.websocketdemo.protocol.JsonProtocolOffer;
import io.websocketdemo.protocol.MySessionDescription;
import io.websocketdemo.protocol.Offer;
import pub.devrel.easypermissions.EasyPermissions;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "ucc";
    MyWebsocketClient client;

    private static final int VIDEO_RESOLUTION_WIDTH = 1280;
    private static final int VIDEO_RESOLUTION_HEIGHT = 720;
    private static final int VIDEO_FPS = 30;


    public static final String VIDEO_TRACK_ID = "1";//"ARDAMSv0";
    public static final String AUDIO_TRACK_ID = "2";//"ARDAMSa0";

    //用于数据传输
    private PeerConnection mPeerConnection;
    private PeerConnectionFactory mPeerConnectionFactory;

    //OpenGL ES
    private EglBase mRootEglBase;
    //纹理渲染
    private SurfaceTextureHelper mSurfaceTextureHelper;

    //继承自 surface view
    private SurfaceViewRenderer mLocalSurfaceView;
    private SurfaceViewRenderer mRemoteSurfaceView;

    private VideoCapturer mVideoCapturer;

    VideoTrack mVideoTrack;
    AudioTrack mAudioTrack;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 申请权限
        {
            String[] perms = {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO};
            if (!EasyPermissions.hasPermissions(this, perms)) {
                EasyPermissions.requestPermissions(this, "Need permissions for camera & microphone", 0, perms);
            }
        }

        // 音频
        org.webrtc.voiceengine.WebRtcAudioRecord.setOnAudioSamplesReady(new WebRtcAudioRecord.WebRtcAudioRecordSamplesReadyCallback() {
            @Override
            public void onWebRtcAudioRecordSamplesReady(WebRtcAudioRecord.AudioSamples audioSamples) {
                audioSamples.getData();
            }
        });

        client = new MyWebsocketClient() {
            @Override
            public void onMyMessage(String message) {
                super.onMyMessage(message);
                Log.w(TAG, "===================" + message);
                Gson gson = new Gson();

                JsonProtocolAnswer resp = gson.fromJson(message, JsonProtocolAnswer.class);
                MySessionDescription answer = resp.answer.answer;

                SessionDescription remoteSDP = new SessionDescription(
                        SessionDescription.Type.fromCanonicalForm(answer.type.toUpperCase()),
                        answer.sdp);
                mPeerConnection.setRemoteDescription(new MySimpleSdpObserver(), remoteSDP);
            }

            @Override
            public void onMyMessage(ByteBuffer bytes) {
                super.onMyMessage(bytes);
            }
        };

        mRootEglBase = EglBase.create();

        {
            mLocalSurfaceView = findViewById(R.id.LocalSurfaceView);
            mRemoteSurfaceView = findViewById(R.id.RemoteSurfaceView);


            mLocalSurfaceView.init(mRootEglBase.getEglBaseContext(), null);
            mLocalSurfaceView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
            mLocalSurfaceView.setMirror(true);
            mLocalSurfaceView.setEnableHardwareScaler(false);


            mRemoteSurfaceView.init(mRootEglBase.getEglBaseContext(), null);
            mRemoteSurfaceView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
            mRemoteSurfaceView.setMirror(true);
            mRemoteSurfaceView.setEnableHardwareScaler(true);
            mRemoteSurfaceView.setZOrderMediaOverlay(true);
        }

        //创建 factory， pc是从factory里获得的
        mPeerConnectionFactory = createPeerConnectionFactory(this);

        // NOTE: this _must_ happen while PeerConnectionFactory is alive!
        Logging.enableLogToDebugOutput(Logging.Severity.LS_VERBOSE);

        mVideoCapturer = createVideoCapturer();

        mSurfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", mRootEglBase.getEglBaseContext());
        VideoSource videoSource = mPeerConnectionFactory.createVideoSource(false);
        mVideoCapturer.initialize(mSurfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());

        mVideoTrack = mPeerConnectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
        mVideoTrack.setEnabled(true);
        mVideoTrack.addSink(mLocalSurfaceView);

        AudioSource audioSource = mPeerConnectionFactory.createAudioSource(new MediaConstraints());
        mAudioTrack = mPeerConnectionFactory.createAudioTrack(AUDIO_TRACK_ID, audioSource);
        mAudioTrack.setEnabled(true);

        findViewById(R.id.btnHello).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //调用call， 进行媒体协商
                doStartCall();
            }
        });

        findViewById(R.id.btnHello2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 连接websocket
                client.Connect();
            }
        });
    }


    /**
     * 呼叫
     */
    public void doStartCall() {
        Log.i(TAG, "Start Call, Wait ...");
        if (mPeerConnection == null) {
            mPeerConnection = createPeerConnection();
        }

        MediaConstraints mediaConstraints = new MediaConstraints();
        //#mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        //#mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
        mediaConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));

        mPeerConnection.createOffer(new MySimpleSdpObserver() {
            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                super.onCreateSuccess(sessionDescription);

                Log.i(TAG, "Create local offer success");
                mPeerConnection.setLocalDescription(new MySimpleSdpObserver(), sessionDescription);

                Gson gson = new Gson();
                {
                    JsonProtocolOffer object = new JsonProtocolOffer();
                    object.method = "offer";
                    object.offer = new Offer();
                    object.offer.name = "one";
                    object.offer.offer = new MySessionDescription();
                    object.offer.offer.sdp = sessionDescription.description;
                    object.offer.offer.type = sessionDescription.type.toString().toLowerCase();

                    String message = gson.toJson(object);

                    Log.e(TAG, ">>>>>>>>>> " + message);
                    client.send(message);
                }

            }
        }, mediaConstraints);
    }


    /**
     * 创建 PeerConnection
     */
    public PeerConnection createPeerConnection() {
        Log.i(TAG, "Create PeerConnection ...");


        LinkedList<PeerConnection.IceServer> iceServers = new LinkedList<>();

        PeerConnection.IceServer ice_server = PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer();
        iceServers.add(ice_server);

        PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
        // TCP candidates are only useful when connecting to a server that supports
        // ICE-TCP.
        //#rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
        // rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
        // rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
        //#rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
        // Use ECDSA encryption.
        // rtcConfig.keyType = PeerConnection.KeyType.ECDSA;
        // Enable DTLS for normal calls and disable for loopback calls.
        rtcConfig.enableDtlsSrtp = true;
        // rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
        PeerConnection connection = mPeerConnectionFactory.createPeerConnection(rtcConfig, new MyPeerConnectionObserver());
        if (connection == null) {
            Log.e(TAG, "Failed to createPeerConnection !");
            return null;
        }

         List<String> mediaStreamLabels = Collections.singletonList("ARDAMS");
         connection.addTrack(mVideoTrack, mediaStreamLabels);
         connection.addTrack(mAudioTrack, mediaStreamLabels);

        return connection;
    }

    /**
     * 创建 PeerConnectionFactory
     */
    public PeerConnectionFactory createPeerConnectionFactory(Context context) {

        VideoEncoderFactory encoderFactory = new MyDefaultVideoEncoderFactory(mRootEglBase.getEglBaseContext(), false, true);
        VideoDecoderFactory decoderFactory = new DefaultVideoDecoderFactory(mRootEglBase.getEglBaseContext());

        PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(context)
                .setEnableInternalTracer(true)
                .createInitializationOptions());

        PeerConnectionFactory.Builder builder = PeerConnectionFactory.builder()
                .setVideoEncoderFactory(encoderFactory)
                .setVideoDecoderFactory(decoderFactory);
        builder.setOptions(null);

        return builder.createPeerConnectionFactory();
    }

    /*
     * Read more about Camera2 here
     * https://developer.android.com/reference/android/hardware/camera2/package-summary.html
     **/
    private VideoCapturer createVideoCapturer() {
        if (Camera2Enumerator.isSupported(this)) {
            return createCameraCapturer(new Camera2Enumerator(this));
        } else {
            return createCameraCapturer(new Camera1Enumerator(true));
        }
    }

    private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();

        // First, try to find front facing camera
        Log.d(TAG, "Looking for front facing cameras.");
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating front facing camera capturer.");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, new MyCameraEventsHandler());
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }

        // Front facing camera not found, try something else
        Log.d(TAG, "Looking for other cameras.");
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating other camera capturer.");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        return null;
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }


    @Override
    protected void onResume() {
        super.onResume();
        mVideoCapturer.startCapture(VIDEO_RESOLUTION_WIDTH, VIDEO_RESOLUTION_HEIGHT, VIDEO_FPS);
    }

    @Override
    protected void onPause() {
        super.onPause();
        try {
            mVideoCapturer.stopCapture();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //doLeave();
        mLocalSurfaceView.release();
        mRemoteSurfaceView.release();
        mVideoCapturer.dispose();
        mSurfaceTextureHelper.dispose();
        PeerConnectionFactory.stopInternalTracingCapture();
        PeerConnectionFactory.shutdownInternalTracer();
        mPeerConnectionFactory.dispose();
    }

}